(function($) {

	$.fn.getBusInstance = function(){
		return KOR.ProductView.getInstanceOf($(this).closest('[data-bus]').attr('data-bus'), {
			domainModel: KOR.Product,
			dataFactoryMethod: KOR.ObjectFactory.getProduct
		});
	}

	KOR.ProductView = KOR.createClass(KOR.Multiton, {

		altImagesTemplate: 'productAltImagesTemplate',
		listPriceTemplate: 'productListPriceTemplate',
		salePriceTemplate: 'productSalePriceTemplate',

		// DOM Hooks for list and sale prices
		productListPriceSelector: '.kor-product-list-price',
		productSalePriceSelector: '.kor-product-sale-price',
		priceHiddenFieldSelector: '.kor-product-price-validation',
		priceHiddenFieldClassName: 'kor-product-price-validation',
		inactiveLabelClassName: 'kor-inactive-label',
		activeCartButtonClassName: 'ws-add-to-cart-active',
		inactiveCartButtonClassName: 'ws-add-to-cart-inactive',
		cartButtonSelector: '.kor-cart-button',

		init: function(selector, mixin) {
			var obj = this;
			obj._super(selector, mixin);
			obj.registerObservers();
		},

		/**
		 *@protected
		 */
		registerObservers: function(){
			//LABEL ui component
			//observers for model attribute change events
            this.observe('changeAttribute', this.changeEventLabelHandler, this);

			//SWATCH ui component
			//swatch observers for model attribute change events
			this.observe('changeAttribute', this.changeEventSwatchHandler, this);
			//swatch observers for model attribute availibility change events
			this.observe('changeAttributeAvailibility', this.changeAvailableEventSwatchHandler, this);

			//SELECT ui component
			//swatch observers for model attribute change events
			this.observe('changeAttribute', this.changeEventSelectHandler, this);
			//swatch observers for model attribute availibility change events
			this.observe('changeAttributeAvailibility', this.changeAvailableEventSelectHandler, this);

            //hidden fields model attribute change event observer
            this.observe('changeAttribute', this.changeAttributeHiddenFieldHandler, this);

			//listen for changes in dynamic blocks
			this.observe('changeDynamicBlock', this.changeDynamicBlockHandler, this);

            //update sku field if one active sku
            this.observe('changeAttributeComplete', this.changeAttributeCompleteSkuHandler, this);
            //update quantity field if one active sku
            this.observe('changeAttributeComplete', this.changeAttributeCompleteQuantityHandler, this);
			//update warranty field if one active sku 
			this.observe('changeAttributeComplete', this.changeAttributeCompleteWarrantyHandler, this);
            //update all product links if one active sku
			this.observe('changeAttributeComplete', this.changeAttributeCompleteLinkHandler, this);

            //price label observer
			this.observe('changeProductPrice', this.changeProductPriceLabelHandler, this);
			//price label observer
			this.observe('changeProductPrice', this.changePriceHiddenFieldHandler, this);

			//main product image
			this.observe('changeProductImage', this.changeProductImageMainImageHandler, this);
			//enhanced image link
			this.observe('changeProductImage', this.changeProductImageEnhancedImageLinkHandler, this);
			//alt image group selector
			this.observe('changeProductImage', this.changeImageGroupSelectedAlternateImageHandler, this);
			this.observe('changeImageGroup', this.changeImageGroupAlternateImagesHandler, this);
			//zoom-rotate image
			this.observe('changeProductImage', this.changeProductImageInlineZoomHandler, this);
			//update the slider
			this.observe('changeImageSets', this.changeSliderNotchesHandler, this);
			this.observe('changeProductImage', this.changeProductImageFlyoutZoomHandler, this);
			$(document).delegate('[data-product]', 'validator:failed validator:success', $.proxy(this, 'updateCartButton'));
		},

		/**
		 *@protected
		 */
		updateCartButton: function(ev){
			if ($(ev.target).hasClass(this.priceHiddenFieldClassName)){
				var data = {target:{id:$(ev.currentTarget).attr('data-product')}}
				this.find(this.priceHiddenFieldSelector,data).data('kor-failed-validation') ? this.find(this.cartButtonSelector,data).removeClass(this.activeCartButtonClassName).addClass(this.inactiveCartButtonClassName).attr('disabled', 'disabled') : this.find(this.cartButtonSelector,data).removeClass(this.inactiveCartButtonClassName).removeAttr('disabled').addClass(this.activeCartButtonClassName);
			}
		},

		/**
		 *@protected
		 */
		changeDynamicBlockHandler: function(ev) {
			if (ev.message.blockData !== null) {
				KOR.info('Replacing ' + ev.message.blockName + ' (' + ev.message.blockID + ')');
				this.find("[data-dynamic-block-id='{{}}']".uInject(ev.message.blockID), ev).html(ev.message.blockData);
			}
		},

        /**
		 *@protected
		 */
		changeEventLabelHandler: function(ev){
            var modelEvent = ev.message.attribute;
			this.find("[data-variation-attribute-label='{{}}']".uInject(modelEvent), ev)
				.html(ev.target.getSelectedAttributeVariationDisplayName(modelEvent) || '');
		},

        /**
		 *@protected
		 */
		changeEventSelectHandler: function(ev){
            var modelEvent = ev.message.attribute;
			this.find("select[data-variation-attribute='{{}}']".uInject(modelEvent), ev)
				.val(ev.target.getSelectedAttributeVariationDisplayName(modelEvent) || "").trigger('focus').trigger('blur');
		},

		changeAvailableEventSelectHandler: function(ev){
			var obj = this;
            var modelEvent = ev.message.attribute;
			obj.find("select[data-variation-attribute='{{}}'] option".uInject(modelEvent), ev).each(function(){
				var option = $(this);
				if(ev.target.isSelectable(modelEvent, option.val()) || option.val() == 'null'){
					option.removeAttr('disabled').removeClass('kor-disabled kor-select-option-disabled')
				}else{
					option.attr('disabled', 'disabled').addClass('kor-disabled kor-select-option-disabled')
				}
			});
		},

		/**
		 *@protected
		 */
		changeEventSwatchHandler: function(ev){
            var modelEvent = ev.message.attribute;
			this.find("[data-variation-attribute='{{}}'] [data-variation-value]".uInject(modelEvent), ev).removeClass('kor-product-swatch-selected');
			this.find("[data-variation-attribute='{{}}'] [data-variation-value='{{}}']".uInject(modelEvent, ev.target.getSelectedAttributeVariationDisplayName(modelEvent)), ev).addClass('kor-product-swatch-selected');
		},

		changeAvailableEventSwatchHandler: function(ev) {
			var obj = this;
            var modelEvent = ev.message.attribute;
			obj.find("[data-variation-attribute='{{}}'] [data-variation-value]".uInject(modelEvent), ev).each(function(v){
				var node = $(this);
				if(ev.target.isSelectable(modelEvent, node.attr('data-variation-value'))){
					obj.enableSwatchAvailability(node);
				}else{
					obj.disableSwatchAvailability(node);
				}
			});
		},

        changeAttributeHiddenFieldHandler : function(ev) {
            var node = this.find("input[type=hidden][data-variation-attribute='{{}}']".uInject(ev.message.attribute), ev);
            if(node.length){
                node.val(ev.target.getSelectedAttributeVariationDisplayName(ev.message.attribute)).trigger('focus').trigger('blur');
            }
        },

        changeAttributeCompleteSkuHandler: function(ev){
            this.find('[name=SKU]', ev).val(
                ev.target.getCartableProductVariant() ? ev.target.getCartableProductVariant().productSKU : ev.target.data.productSKU
            );
        },

        changeAttributeCompleteQuantityHandler: function(ev) {
            this.find('.kor-product-qty', ev).attr('name', 'Quantity_{{}}'.uInject(
                ev.target.getCartableProductVariant() ? ev.target.getCartableProductVariant().productSKU : ev.target.data.productSKU
            ));
        },

		changeAttributeCompleteWarrantyHandler: function(ev) {
            this.find('.kor-product-warranty', ev).attr('name', 'DependentSKU_{{}}'.uInject(
                ev.target.getCartableProductVariant() ? ev.target.getCartableProductVariant().productSKU : ev.target.data.productSKU
            ));
        },
		changeAttributeCompleteLinkHandler: function(ev){
			var variant = ev.target.getCartableProductVariant() ? ev.target.getCartableProductVariant().productSKU : ev.target.data.productSKU
			$.each(this.find('.kor-product-link', ev), function(index, link){
				link.href = link.href.replace(/(sku=)[^&]+/i, '$1'+ variant)
			})
		},

        changeProductPriceLabelHandler: function(ev){
			var price = ev.target.getProductPrice();
			var productListPrice = this.find(this.productListPriceSelector, ev);
			var productSalePrice = this.find(this.productSalePriceSelector, ev);

			productListPrice.uTemplate(this.listPriceTemplate, price);
			productSalePrice.uTemplate(this.salePriceTemplate, price);
		},

		changePriceHiddenFieldHandler: function(ev){
			var price = ev.target.getCartablePrice();
			var priceHiddenField = this.find(this.priceHiddenFieldSelector, ev);
			priceHiddenField.val(price).change().keyup();
		},

		changeProductImageMainImageHandler: function(ev){
            this.find('.kor-product-photo img', ev).each(function() {
				var node = $(this),
					type = node.attr('data-type');
				if (type) {
					var image = ev.target.getProductImage(type);
					if(image) {
						node.attr('src', image.src);
					}
				}
			});
        },

		changeImageGroupAlternateImagesHandler: function(ev){
			var prodView = this;
			this.find('.kor-product-alt-images', ev).each(function() {
				var node = $(this);
				var imageViewListString = node.attr("data-view-ids") || "";
				var selectedImageViews = imageViewListString.split(",");
				var productImages = ev.target.getProductImagesThumbs(node.attr('data-type'));
				var selectedProductImages = [];
				if(imageViewListString.trim()==''){
					selectedProductImages = productImages;
				} else {
					for(var i=0; i<selectedImageViews.length; i++) {
						for(var j=0; j<productImages.length; j++) {
							if(selectedImageViews[i] == productImages[j].view) {
								selectedProductImages[selectedProductImages.length]=productImages[j];
								break;
							}
						}
					}
				}
				
				node.uTemplate(prodView.altImagesTemplate, {
					images: selectedProductImages
				});
			});
        },

        changeImageGroupSelectedAlternateImageHandler: function(ev){
			var view = ev.target.currentView;
			this.find('.kor-product-alt-images img', ev).each(function() {
				var node = $(this);
				if(node.attr('data-view-value') == view){
					node.closest('li').addClass("kor-selected");
				} else {
					node.closest('li').removeClass("kor-selected");
				}
			});
		},

		changeProductImageEnhancedImageLinkHandler: function(ev){
			var node = this.find('.kor-dynamic-image-link', ev);
			if(node.length>0){
				var image = ev.target.getEnhancedProductImage(node.attr('data-type'));
				if (!node.data('behaviorClass')) {
					node.data('behaviorClass', node.attr('class'));
				}
				if (image && image.view == ev.target.getProductImage().view){
					node.attr('href', image.src)
						.removeAttr('data-dialog')
						.attr('class', node.data('behaviorClass'))
						.removeClass('kor-disabled');
				} else {
					node.removeAttr('data-dialog').attr('href', 'javascript:void(false)').attr('class', '').addClass('kor-disabled').addClass('kor-dynamic-image-link');
				}
			}
        },

		changeProductImageFlyoutZoomHandler: function(ev){
			var zrElem = this.find('.kor-zoom-rotate[data-zoom-behavior=flyout]', ev);
			if (zrElem) {
				var zoomImage = ev.target.getProductZoomImage();
				if(zoomImage){
					var baseImage = zrElem.find('img.kor-zoom-default').attr('src');
					zrElem.trigger('flyoutZoom:loadImageGroup', {base: baseImage, 'zoom': zoomImage.src});
				} else {
					zrElem.trigger('flyoutZoom:loadImageGroup', null);
				}
			}
		},

		changeProductImageInlineZoomHandler: function(ev) {
			var zrElem = this.find('.kor-zoom-rotate[data-zoom-behavior=inlinezoom]', ev);
			if (zrElem) {
				var inst = zrElem.data('inlineZoom-instance') || zrElem.focus().data('inlineZoom-instance');
				if (inst) {
					var zoomImage = ev.target.getStrictProductImage(zrElem.attr('data-zoom-image-type'));
					if (zoomImage) {
						KOR.info("Found zoom image: ", zoomImage);
						var height = zoomImage.height > 0 ? zoomImage.height : zoomImage.imageTypeHeight,
							width = zoomImage.width > 0 ? zoomImage.width : zoomImage.imageTypeWidth;
						if (height && width) {
							inst.configure({
								image: zoomImage.src,
								zoomDimensions: {
									width: width,
									height: height
								}
							});
							return true;
						}
						else {
							KOR.warn("Zoom disabled; height and width dimensions missing in zoom image data.");
						}
					}
					else {
						KOR.info("Zoom disabled; No zoom image found for current product view");
					}
					inst.configure({image: null});
				}
			}
			return false;
		},

		changeSliderNotchesHandler: function(ev) {
			var sliderElems = this.find('.kor-range-slider', ev),
				obj = this;
			$.each(sliderElems, function(idx, sliderElem) {
				sliderElem = $(sliderElem);
				var inst = sliderElem.data('rangeSlider-instance'),
					size = obj.find('.kor-product-photo img[data-type]', ev).attr('data-type');
				if (!inst) {
					sliderElem.focus();
					inst = sliderElem.data('rangeSlider-instance');
				}
				if (inst) {
					inst.configure({
						high: ev.target.getNumImagesInSet(size) - 1
					});
				}
			});
		},

		handleRotateControl: function(ev) {
			var size = this.find('.kor-product-photo img[data-type]', ev).attr('data-type');
			ev.target.changeAttributeView(ev.target.getViewAtSetPosition(ev.message, size));
		},

        fire: function(name, message, node){
			this.getDomainModel({
				name:name,
				message:message,
				node:node,
				callback: $.proxy(this, 'fireOnDomainModel')
			});
		},

		getDomainModel: function(domEventObj){
			var obj = this;
			domEventObj.id = domEventObj.node.attr('data-product');
			if(this.dataFactoryMethod){
				this.dataFactoryMethod(domEventObj.id, function(data){
					obj.domainModelCallback(data, domEventObj)
				});
			} else {
				obj.domainModelCallback(data, domEventObj)
			}
		},

		domainModelCallback: function(data, domEventObj){
			domEventObj.model = this.domainModel.get(domEventObj.id, data, this)
			if(!domEventObj.node.data('product-instance')){
				domEventObj.node.data('product-instance', domEventObj.model);
				$.each($('[data-variation-attribute]'), function(i,v){
					var attVal = $(v).val()
					if(attVal){
						domEventObj.model.changeAttributeDefault($(v).attr('data-variation-attribute'), attVal);
					}
				})
			}
			domEventObj.callback.call(this, domEventObj);
		},

		fireOnDomainModel: function(domEventObj){
			domEventObj.model.changeAttribute(domEventObj.name, domEventObj.message);
		},

		find: function(selector, ev) {
			var obj = this;
			var matches = $([]);
			$(selector).each(function() {
				var node = $(this);
				var productDom = node.closest('[data-product]');
				if(productDom.length && (productDom.attr('data-bus') == obj.selector || productDom.closest('[data-bus]').attr('data-bus') == obj.selector) && productDom.attr('data-product') == ev.target.id){
					matches = matches.add(node);
				}
			});

			return matches;
		},

		enableSwatchAvailability: function(node){
			node.fadeTo(300, 1).removeClass('kor-disabled').addClass('kor-enabled');
		},

		disableSwatchAvailability: function(node){
			node.fadeTo(300, .1).addClass('kor-disabled').removeClass('kor-enabled').removeClass('kor-selected');
		}
	});

	KOR.ProductView.domToBusEventHandler = function(ev, data, eventName){
        ev.preventDefault()
		var node = $(ev.currentTarget);
		node.getBusInstance().fire(
			eventName || node.closest('[data-variation-attribute]').attr('data-variation-attribute'),
			node.attr('data-view-value') || node.attr('data-variation-value') || node.val() || node.html(),
			node.closest('[data-product]')
		);
	};

	KOR.ProductView.domToModelHandler = function(ev, data, callback){
		var node = $(ev.currentTarget);
		var obj = node.getBusInstance();
		obj.getDomainModel({
			node: $(node).closest('[data-product]'),
			callback: callback
		});
	};

	KOR.ProductView.registerDomEvents = function(){
		var obj = this;

		//select dom events that fire change events of the model bus
		$(document).delegate('[data-bus] select[data-variation-attribute]', 'change', this.domToBusEventHandler);

		//swatch dom events that fire change events of the model bus
		$(document).delegate('[data-bus] [data-variation-attribute] [class!=\'kor-disabled\'][data-variation-value]', 'click',  this.domToBusEventHandler);

		//alt images dom events that fire change events of the model bus
		$(document).delegate('[data-bus] .ws-alternate-views-list-link [data-view-value]', 'click', function(ev, data){
			obj.domToBusEventHandler(ev, data, 'view')
		});

		//initialize product on rollover of image manipulators
		$(document).delegate('.kor-range-slider, .kor-zoom-button, .kor-zoom-rotate', 'mouseenter', function(ev, data) {
			obj.domToModelHandler(ev, data, function(domEventObj) {
				return;
			});
		});

		//roll over of flyout zoom image
		$(document).delegate('.kor-zoom-rotate[data-zoom-behavior=flyout]', 'mouseenter', function(ev, data){
			obj.domToModelHandler(ev, data, function(domEventObj){
				this.changeProductImageFlyoutZoomHandler({target:domEventObj.model})
			})
		});
	};

})(jQuery);
